const fs = require('fs');
const { def, base, intField, setEvent, regFieldGetSet, regEvent } = require("./jsdemo_windows.common.cpp.js");

const windows_def = String.raw/* cpp */`
struct Windows{
  int width;
  int height;
  Window* topChild;
  Window* topWindow;
  Window* mouseDownObj;
  int oldbtn1;
  int oldbtn2;
  int mousex;
  int latchX;
  int mousey;
  int screenBase;
  ArrayList* windows;
  int needRefresh;
  void render();
  void procEvent();
  void mouseEvent(int btn,int state);
  void keyEvent(int key, int chr);
};
`;

const windows = String.raw/* cpp */`

  IRQ_PROC void JS_irq_proc(){
    IRQ_PROLOG;
    
    //ldw	r2, %gprel(irqPtr)(gp)
    //callr	r2
    __asm__ __volatile__("call JS_irqProc");
    
    IRQ_EPILOG;
  }

  void Windows_init(Windows* p, int screenBase, int width, int height){
    p->oldbtn1 = 0;
    p->oldbtn2 = 0;
    //IOWR(VGA, VGA_MODE, 1);//1024 * 768
    IOWR(VGA, VGA_MODE_BLOCK, (0<<1) | (0<<0));//1024=2Mbyte
    IOWR(VGA, VGA_BASE, screenBase);//1024=2Mbyte
    IOWR(VGA_BLT, BLT_S_BASE_ADDR, screenBase);//1024=2Mbyte
    
    p->screenBase = screenBase;
    //screen_base = screenBase*2048;
    p->windows = new ArrayList();
    p->topWindow = 0;
    IOWR(VGA_BLT, BLT_S_SWITCH, SWITCH_SCREEN_MEM_03 | 0);// 0: screen 1:mem
    IOWR(VGA_BLT, BLT_S_SWITCH, SWITCH_DATA_YY_01 | 0);// 0:data 1:YY
    IOWR(VGA_BLT, BLT_S_SWITCH, SWITCH_TRANS_OFF_04);//transparent off
    paint(0, 0, width, height, 0x0000);
    p->width = width;
    p->height = height;
    p->mouseDownObj = 0;


    loadCur();

    int value;
    int pos;
    // value = (int)_irq_timer;
    // pos = 0;
    // __asm__ __volatile__("setirq %[input1],%[input2]"::[input1]"r"(value),[input2]"r"(pos));//value, pos

    
    
    //setIrq(irq_proc, 1);
    //setirq reg, reg              @          52 @                      3 @  01011 @ value, pos
    value = (int)JS_irq_proc;
    pos = 2;
    __asm__ __volatile__("setirq %[input1],%[input2]"::[input1]"r"(value),[input2]"r"(pos));//value, pos

    int status = 1;
    __asm__ __volatile__("sti %[input1],%[input2]":[input1]"=r"(status):[input2]"r"(status));//value, pos


  }


  static JSValue js_windows_addWindow(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Windows *windows = (Windows*)JS_GetOpaque(this_val);
    if (!JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "Window"))) {
      return JS_UNDEFINED;
    }
    Window *window = (Window*)JS_GetOpaque(argv[0]);
    if(windows && window){
      windows->windows->push(window);
      JS_DupValue(ctx, argv[0]);
      window->setWindows(windows);
      windows->topWindow = window;
    }
    return JS_UNDEFINED;
  }
  static JSValue js_windows_removeWindow(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Windows *windows = (Windows*)JS_GetOpaque(this_val);
    if (!JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "Window"))) {
      return JS_UNDEFINED;
    }
    Window *window = (Window*)JS_GetOpaque(argv[0]);
    windows->windows->remove(window);
    JS_FreeValue(ctx, argv[0]);
    windows->mouseDownObj = 0;
    windows->topWindow = 0;
    return JS_UNDEFINED;
  }

  static JSValue js_windows_setTopWindow(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Windows *windows = (Windows*)JS_GetOpaque(this_val);
    if (!JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "Window"))) {
      return JS_UNDEFINED;
    }
    Window *window = (Window*)JS_GetOpaque(argv[0]);
    windows->topWindow = window;
    return JS_UNDEFINED;
  }
    // void removeWindow(Window* window);
    // void setTopWindow(Window* window);

    // JS_SetPropertyStr(ctx, windows_proto, "removeWindow", JS_NewCFunction(ctx, js_windows_removeWindow, "removeWindow", 1));
    // JS_SetPropertyStr(ctx, windows_proto, "setTopWindow", JS_NewCFunction(ctx, js_windows_setTopWindow, "setTopWindow", 1));

  static JSValue js_windows_refresh(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Windows *windows = (Windows*)JS_GetOpaque(this_val);
    if(windows){
      windows->needRefresh = 1;
    }
    return JS_UNDEFINED;
  }
  


  void Windows::render(){
    if(needRefresh){
      //printf("refresh All\n");
      paint(0, 0, this->width, this->height, 0x0000);
      //TODO 换缓冲区
    }

    //TODO zIndex
    for(int i = 0; i< windows->length;i++){
      Window* p = (Window*)windows->get(i);
      if(needRefresh){
        p->needRefresh = 1;
      }
      if(p!=topWindow){
        p->render(0xFFFF);
      }
    }
    if(topWindow){
      Window* p = topWindow;
      if(needRefresh){
        p->needRefresh = 1;
      }
      p->render(0xFFFF);
    }
    needRefresh = 0;
  }


  static int hidFIFO[16];
  static int hidFIFObtm = 0;
  static int hidFIFOidx = 0;

  void JS_setFifo(int val){
    hidFIFOidx++;
    hidFIFOidx&=15;
    hidFIFO[hidFIFOidx]=val;
  }

  int JS_getFifo(){
    //sti(0);
    int status;
    int ival = 0;
    __asm__ __volatile__("sti %[input1],%[input2]":[input1]"=r"(status):[input2]"r"(ival));//value, pos

    int val = 0;
    if(hidFIFObtm!=hidFIFOidx){
      hidFIFObtm++;
      hidFIFObtm&=15;
      val = hidFIFO[hidFIFObtm];
    }
    //sti(1);
    __asm__ __volatile__("sti %[input1],%[input2]":[input1]"=r"(status):[input2]"r"(status));//value, pos
    return val;
  }

  extern "C"{
    void JS_irqProc(){
      //int irq = IORD(IRQ, 0);
      ///if(irq&1){
      ///}
      //if(irq&4){
        int hid_value = IORD(HID, 0);
        IOWR(HID, 0, 0);
        JS_setFifo(hid_value);
        
      //}
      IOWR(IRQ, 0, 4);
    }
  }


  void Windows::procEvent(){

    int hid_value = JS_getFifo();
    int flg = (hid_value & 0xFF000000);
    int ismouse = 0;
    if(flg == 0x81000000){
      mousey-=(char)( hid_value     & 0x000000FF);
      mousex+=(char)((hid_value>>8) & 0x000000FF);
      ismouse = 1;
    }
    else if(flg == 0x84000000){
      mousey+=(char)( hid_value     & 0x000000FF);
      mousex+=(char)((hid_value>>8) & 0x000000FF);
      ismouse = 1;
    }
    else if(flg == 0x8E000000){ //debugger模块，绝对x，
      latchX=hid_value & 0x0000FFFF;
      ismouse = 1;
    }
    else if(flg == 0x8F000000){ //debugger模块，绝对y，
      mousex=latchX;
      mousey=hid_value & 0x0000FFFF;
      ismouse = 1;
    }
    else if(hid_value & 0x80000000){
      int key = getKey(hid_value);
      int chr = getchar(key);
      keyEvent(key, chr);
    }

    if(ismouse){
      
      //mouse
      int btn1 = hid_value & 0x00010000;
      int btn2 = hid_value & 0x00020000;

      //int btn3 = hid_value & 0x00040000;//mid
      if(mousex<0){
        mousex=0;
      }
      if(mousex>(width-1)){
        mousex=(width-1);
      }

      if(mousey<0){
        mousey=0;
      }
      if(mousey>(height-1)){
        mousey=(height-1);
      }
      IOWR(VGA, VGA_CURSOR_X, mousex);
      IOWR(VGA, VGA_CURSOR_Y, mousey);
      
      if(oldbtn1 != btn1){
        if(btn1){
          mouseEvent(1,1);//down
        }else{
          mouseEvent(1,0);//up
        }
      }
      if(oldbtn2 != btn2){
        if(btn2){
          mouseEvent(2,1);//down
        }else{
          mouseEvent(2,0);//up
        }
      }
      {
        mouseEvent(0,0);//up
      }
      oldbtn1 = btn1;
      oldbtn2 = btn2;
      //oldbtn3 = btn3;
    }
  }

  void Windows::mouseEvent(int btn, int state){
    if(btn && !state){
      if(mouseDownObj){
        Window* win = mouseDownObj;
        int x = win->abs_pos_x;
        int y = win->abs_pos_y;
        win->mouseUp(mousex-x, mousey-y, btn);
        mouseDownObj = 0;
      }
    }else{
      for(int i = 0; i< windows->length;i++){
        Window* win = (Window*)windows->get(i);
        int x = win->left;
        int y = win->top;
        int w = win->width;
        int h = win->height;
        if(mousex>=x && mousex<x+w && mousey>=y && mousey<y+h){
          if(btn && state){
            if(win!=topWindow){
              //setTopWindow(win);
              topWindow = win;
              //refresh();
              needRefresh = 1;
            }
            win->mouseDown(mousex-x, mousey-y, btn);
            mouseDownObj = win;//zIndexCache[i];
          }else{
            win->mouseMove(mousex-x, mousey-y, btn);
          }
          break;
        }
      }
      
      
    }
  }

  void Windows::keyEvent(int key, int chr){
    if(topWindow){
      topWindow->keyEvent(key, chr);
    }
  }
`;

const { _console } = require("./jsdemo_console.cpp.js");
const { file } = require("./jsdemo_file.cpp.js");
const { core } = require("./jsdemo_core.cpp.js");
const { util } = require("./jsdemo_util.cpp.js");
const { lib3d } = require("./jsdemo_lib3d.cpp.js");

const { window, window_def } = require("./jsdemo_windows.window.cpp.js");

const { button } = require("./jsdemo_windows.button.cpp.js");
const { form } = require("./jsdemo_windows.form.cpp.js");
const { label } = require("./jsdemo_windows.label.cpp.js");
const { input } = require("./jsdemo_windows.input.cpp.js");


const reg = String.raw/* cpp */`
  Windows* theWindows;
  void windows_init() {
    JS_NewClassID(&js_windows_class_id);
    JSClassDef windows_def = {
      .class_name = "Windows",
    };
    JS_NewClass(rt, js_windows_class_id, &windows_def);

    JSValue windows_proto = JS_NewObject(ctx);
    JS_SetPropertyStr(ctx, windows_proto, "refresh", JS_NewCFunction(ctx, js_windows_refresh, "refresh", 0));
    JS_SetPropertyStr(ctx, windows_proto, "addWindow", JS_NewCFunction(ctx, js_windows_addWindow, "addWindow", 1));
    JS_SetPropertyStr(ctx, windows_proto, "removeWindow", JS_NewCFunction(ctx, js_windows_removeWindow, "removeWindow", 1));
    JS_SetPropertyStr(ctx, windows_proto, "setTopWindow", JS_NewCFunction(ctx, js_windows_setTopWindow, "setTopWindow", 1));


    JS_SetClassProto(ctx, js_windows_class_id, windows_proto);

    Windows* p = new Windows();
    theWindows = p;
    int memSize = getMemSize();
    int vpos = memSize - VIDEO_RAM_SIZE;
    vpos = vpos/1024/2;//lines

    Windows_init(p, vpos, 1024, 768);

    JSValue obj = JS_NewObjectClass(ctx, js_windows_class_id);
    JS_SetOpaque(obj, p);
    //JSValue list = JS_NewArray(ctx);
    //JS_SetPropertyStr(ctx, obj, "_list", list);
    JS_SetPropertyStr(ctx, global, "windows", obj);

    JSValue window_proto;
    JSValue window_ctor;
    window_init(&window_ctor, &window_proto);
    button_init(&window_ctor, &window_proto);
    form_init(&window_ctor, &window_proto);
    label_init(&window_ctor, &window_proto);
    input_init(&window_ctor, &window_proto);
  }
`;


const main_js_code = fs.readFileSync("jsdemo_main.js").toString();
//const main_js_code = fs.readFileSync("jsdemo_test.js").toString();

const main_code = String.raw/* cpp */`

  int proc();

  int main(){
    int memSize = getMemSize();
    memSize -= VIDEO_RAM_SIZE * 2;
    __asm__ __volatile__("add sp, r0, %[input1]"::[input1]"r"(memSize));
    proc();
  }





  int proc(){
    systemInit();
    memInit(VIDEO_RAM_SIZE * 2);
    initStdio();

    int res = (int)__getRes__();
    char_table = (int*)(res + CHAR_TABLE);
    gb_char_table = (int*)(res + HZK);
    u2gb = (int*)(res + U2GB);
    //void* frameBuffer;

    int memSize = getMemSize();

    printf("hello from c++.\r\n");
    printf("memSize:%dM\r\n", memSize/1024/1024);
    int free = tlsf_get_free_size();
    printf("memAvail:%dk\r\n", free/1024);


    js_init();
    console_init();
    file_init();
    util_init();
    windows_init();
    lib3d_init();
    
    {
      const char *script = R"--(
${main_js_code}
  )--";
      // 执行脚本
      exec(script);
    }

    while(1){
      theWindows->render();
      check_timers(ctx);
      theWindows->procEvent();
      JS_RunGC(rt);
    }


  }
`;



fs.writeFileSync("jsdemo.cpp", def +
  core +
  file +
  util +
  _console +
  lib3d +
  windows_def + window_def + base +
  windows +
  window +
  button +
  form +
  label +
  input +
  reg + main_code, "utf-8");




/*


#include "quickjs.h"

int mainx() {
    JSRuntime *rt = JS_NewRuntime();
    JSContext *ctx = JS_NewContext(rt);

    // 注册 class
    JS_NewClassID(&js_mybuffer_class_id);
    JS_NewClass(rt, js_mybuffer_class_id, &js_mybuffer_class);

    // 构造函数
    JSValue global = JS_GetGlobalObject(ctx);
    JSValue ctor = JS_NewCFunction(ctx, js_new_mybuffer, "MyBuffer", 1);
    JS_SetPropertyStr(ctx, ctor, "size", JS_NewCFunction(ctx, js_mybuffer_size, "size", 0));
    JS_SetPropertyStr(ctx, global, "MyBuffer", ctor);
    JS_FreeValue(ctx, global);

    // JS 代码：创建并使用对象
    const char *script =
        "let b = new MyBuffer(10);\n"
        "console.log('size =', b.size());\n";

    JS_Eval(ctx, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);

    // 触发 GC
    JS_RunGC(rt);

    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

*/